/*
* VFS_Group 19 - IIIT-B, MTech Program, 1st Semester, file_name Management.
*
* Header file having prototype of structure for NArray List and all operations
* on it. NArrayTree
*
*/

#ifndef STANDARD_IO
#define STANDARD_IO

#include<stdio.h>

#endif

#ifndef STANDARD_LIB
#define STANDARD_LIB

#include<stdlib.h>

#endif

#ifndef STRING_LIB
#define STRING_LIB

#include<string.h>
#include "../include/string_operations.h"

#endif

#include "../include/global_header.h"


// Operations supported by N-Array file_name Structure.
nary_pointer initialize_nary() {

    nary_pointer newNode = (nary_pointer) malloc(sizeof(nary_node));
	strcpy(newNode->file_name,"root/");
	newNode->child = NULL;
	newNode->sibling = NULL;
	newNode->parent = NULL;
	newNode->block_num = -1;
    newNode->type = 'd';

    root_dir = newNode;

	return newNode;
}

nary_pointer modified_insert_nary(nary_pointer pwd, char* name, long int block, char type) {

    nary_pointer temp_pwd = pwd->child;

    while(temp_pwd != NULL){

        if(strcmp(temp_pwd->file_name,name) == 0)
            break;

        temp_pwd = temp_pwd->sibling;
    }

    if(temp_pwd == NULL) {
        nary_pointer newNode = (nary_pointer)malloc(sizeof(nary_node));
        strcpy(newNode->file_name,name);
        newNode->block_num = block;
        newNode->child = NULL;
        newNode->type = type;

        newNode->sibling = pwd->child;
        pwd->child = newNode;
        newNode->parent = pwd;

        if(newNode->type == 'f'){

            total_fdcount++;

            return pwd;
        }
        else
        {
            total_fdcount++;

            return newNode;
        }
    }
    else {
        if(temp_pwd->block_num == -1) {
            temp_pwd->block_num = block;
            temp_pwd->type = type;
            total_fdcount++;

            return temp_pwd;
        }
    }
}

void recursive_add(int index) {
   char *ptr,*rest,*token,*temp_token;
   nary_pointer pwd = root_dir;

   ptr= (char*)malloc(256*sizeof(char));
   rest = (char*)malloc(256*sizeof(char));
   token = (char*)malloc(256*sizeof(char));
   temp_token = (char*)malloc(256*sizeof(char));

   strcpy(ptr,fd_array[index].file_name);

	while(token = strtok_r(ptr, "/", &rest)) {
		ptr = rest;
		temp_token = token;

        if(strcmp(token,"root/") != 0) {

            if(*rest == NULL)
                pwd = modified_insert_nary(pwd, token, fd_array[index].block_num, fd_array[index].type);
            else
                pwd = modified_insert_nary(pwd, token, -1, 'd');

        }

	}
}

void rebuild_dir_struct() {

    long int i;


    for(i=0;i<(total_size  * 1024 / (BLOCK_SIZE));i++) {

        if(strcmp(fd_array[i].file_name,"root/") != 0) {


            if(fd_array[i].file_name != NULL && strcmp(fd_array[i].file_name, "") != 0){
                // Insert to BST.

                insert_bst(fd_array[i].file_name);

            }
            recursive_add(i);

        }
    }

}

nary_pointer nary_search(char *name) {

    //printf("Current directory in nary_search :: %s", root_dir->file_name);
    nary_pointer temp = root_dir->child;

    char *rest = (char*)malloc(256*sizeof(char));
    char *token = (char*)malloc(256*sizeof(char));
    while(token = strtok_r(name,"/",&rest)) {
        name = rest;
        while(temp != NULL) {
            if(strcmp(temp->file_name, token) == 0) {
                if(*rest == NULL)
                    return temp;
                temp = temp->child;
                break;
            }
            temp = temp->sibling;
        }
    }
    return temp;
}

int InsertIntoNArray(char *full_path, char* FileName, long int block_num, char type) {

    char *rest = (char*)malloc(256*sizeof(char));
    char *token = (char*)malloc(256*sizeof(char));
    char *temp_full_path = (char*)malloc(256*sizeof(char));
    int i, levels =0, found;

    if(full_path[strlen(full_path) -1 ] == '/'){ // skip last /
        strncpy(temp_full_path, full_path, (strlen(full_path)-1));
    }
    else{
        strcpy(temp_full_path, full_path);
    }


    for(i=0; i< strlen(temp_full_path); i++){
        if(temp_full_path[i] == '/')
            levels++;
    }



    nary_pointer prev_dir = root_dir;
    nary_pointer cur_dir = root_dir->child;


    while(token = strtok_r(temp_full_path, "/", &rest)){

        levels--;

        temp_full_path = rest;



        found = 0;

        while(cur_dir != NULL){
            if(strcmp(cur_dir->file_name, token) == 0){
                found = 1;

                if(levels == 0){
                    //printf("File/folder already exists. \n");
                    return 0;
                }

                break;
            }
            cur_dir = cur_dir->sibling;
        }

        // Intermediate node
        if(found ==0 && levels !=0 ){ // full tree should be built;

                do{
                    nary_pointer newNode1;

                    if(total_fdcount >= (total_size * 1024)/BLOCK_SIZE) {
                        printf("%s\n",ERR_VFS_MAKEDIR_01);                  // FILESYSTEM_FULL
                        return -1;
                    }
                    if(levels != 0){
                        newNode1 = (nary_pointer)malloc(sizeof(nary_node));
                        strcpy(newNode1->file_name,token);
                        newNode1->block_num = -1;
                        newNode1->child = NULL;
                        newNode1->sibling = prev_dir->child;
                        newNode1->parent = prev_dir;
                        newNode1->type = 'd';

                        prev_dir->child = newNode1;

                        total_fdcount++;
                    }

                    if(levels == 0){


                        newNode1 = (nary_pointer)malloc(sizeof(nary_node));
                        strcpy(newNode1->file_name,token);
                        newNode1->block_num = block_num;
                        newNode1->child = NULL;
                        newNode1->sibling = prev_dir->child;
                        newNode1->parent = prev_dir;
                        newNode1->type = type;

                        prev_dir->child = newNode1;

                        total_fdcount++;

                        return 1;

                    }

                    levels --;
                    prev_dir = newNode1;
                    temp_full_path = rest;
                }while(token = strtok_r(temp_full_path, "/", &rest));                                   // NEWLY ADDED
        }

        // Last Node
        if(found ==0 && levels ==0 ){
                if(total_fdcount >= (total_size * 1024)/BLOCK_SIZE) {
                    printf("%s\n",ERR_VFS_MAKEDIR_01);                  // FILESYSTEM_FULL
                    return -1;
                }

                nary_pointer newNode = (nary_pointer)malloc(sizeof(nary_node));
                strcpy(newNode->file_name,token);
                newNode->block_num = block_num;
                newNode->child = NULL;
                newNode->sibling = prev_dir->child;
                newNode->parent = prev_dir;
                newNode->type = type;

                prev_dir->child = newNode;

                total_fdcount++;

                return 1;
        }

        prev_dir=cur_dir;
        cur_dir = cur_dir->child;

    }

}


/* void recursive_delete(nary_pointer cur) {
	if(cur->sibling != NULL) recursive_delete(cur->sibling);
	if(cur->child != NULL) recursive_delete(cur->child);
	free_list[cur->block_num] = 1;                      // 1 - free block
	free(cur);
} */

int nary_delete(char* file_name) {
	nary_pointer parent_dir,prev_dir,cur,prev;
	int success_flag = 0;
	parent_dir = root_dir->child;

    char *rest = (char*)malloc(256*sizeof(char));
    char *token = (char*)malloc(256*sizeof(char));
    char *temp_file_name = (char*)malloc(256*sizeof(char));
    strcpy(temp_file_name, file_name);
    
    if(strcmp(file_name,"/") == 0) {
    	return -1;
    }
    
    while(token = strtok_r(temp_file_name, "/", &rest)) {
		temp_file_name = rest;
		success_flag = 0;
		while(parent_dir != NULL) {
            prev_dir = parent_dir;
            if(strcmp(parent_dir->file_name,token) == 0) {
                parent_dir = parent_dir->child;
                success_flag = 1;
                break;
            }
            parent_dir = parent_dir->sibling;
		}
		if(success_flag == 0) {
            //printf("Invalid path.\n");
            return 0;
		}
    }

    if(prev_dir->parent->child == prev_dir) {
			prev_dir->parent->child = prev_dir->sibling;
			cur = prev_dir;
		}
		else {
			prev = NULL;
			cur = prev_dir->parent->child;
			while(cur != NULL && strcmp(file_name, cur->file_name) != 0) {

				prev = cur;
				cur = cur->sibling;
			}
			if(cur == NULL) {

				return 0;
			}
			prev->sibling = cur->sibling;
		}

		if(cur->child != NULL){ // Directory is not empty.
            return -1;
		}

		free_list[cur->block_num] = 1;
		if(cur != NULL) free(cur);
		return 1; // Success

}

int move_nodes(char *input_path, char *output_path, char type) {
    nary_pointer input,output;
    nary_pointer temp = root_dir->child;
    int found;

    if(temp == NULL) {
        return 0;
    }

    input = (nary_pointer) nary_search(input_path);

    // PARSE OUTPUT NODE
    if(strcmp(output_path,"/") == 0) {
        output = root_dir;
    }
    else {

        output = (nary_pointer) nary_search(output_path);
    }

    if(output->type == 'f' && type == 'd') {

        return -1;
    }

    nary_pointer prev, cur;
    cur = input->parent->child;
    prev = NULL;

    while(cur != input) {
        prev = cur;
        cur = cur->sibling;
    }
    if(prev == NULL) {
        cur->parent->child = cur->sibling;
        cur->parent = output;
        cur->sibling = output->child;
        output->child = cur;
    }
    else {
        prev->sibling = cur->sibling;
        cur->parent = output;
        cur->sibling = output->child;
        output->child = cur;
    }
    
    return 1;
}

// Used to write output of list dir.
FILE* lsdir_out_file = NULL;

void recursive_list(nary_pointer dir) {
    nary_pointer temp;
    if(dir == NULL) return;
    temp = dir;

    fprintf(lsdir_out_file, "%s\t\t\t%c\n",temp->file_name,temp->type);
    recursive_list(temp->sibling);
    if(temp->type == 'd') {

        fprintf(lsdir_out_file,"\n****%s****\n",temp->file_name);
    }
    recursive_list(temp->child);
}

int list_files(char *file_name, int rec_flag, char *save_path) {
    nary_pointer temp = root_dir;
    nary_pointer iterator;
    int found = 0;
    temp = temp->child;

    lsdir_out_file = fopen(save_path,"w");
    if(lsdir_out_file == NULL){
        printf("%s\n", ERR_VFS_LISTDIR_04);         // "CANNOT_CREATE_OUTPUTFILE"
        return 0;
    }

    if(temp == NULL) {
        if(lsdir_out_file != NULL) fclose(lsdir_out_file);
        return 1;
    }

    char *rest = (char*)malloc(256*sizeof(char));
    char *token = (char*)malloc(256*sizeof(char));
    while(token = strtok_r(file_name, "/", &rest)) {
		file_name = rest;
		while(temp != NULL) {
		    if(strcmp(temp->file_name,token) == 0) {
		        found = 1;
		        temp = temp->child;
                break;
		    }
		    temp = temp->sibling;
		}
		if(found == 0) {
            printf("%s\n",ERR_VFS_LISTDIR_01);          // "CANNOT_FIND_SPECIFIED_PATH_OR_DIR"
            break;
		}
    }


    if(rec_flag == 0) {
        iterator = temp;
        while(iterator != NULL) {

            fprintf(lsdir_out_file ,"%s\t\t\t%c\n",iterator->file_name,iterator->type);
            iterator = iterator->sibling;
        }
    }
    else {
        iterator = temp;
        if(temp == NULL){

            if(lsdir_out_file != NULL) fclose(lsdir_out_file);
            return 0;
        }

        fprintf(lsdir_out_file,"\n****%s****\n",temp->parent->file_name);
        recursive_list(iterator);
    }

	if(lsdir_out_file != NULL) fclose(lsdir_out_file);
    return 1;
}

void display(nary_pointer root) {

	if(root->sibling != NULL) display(root->sibling);
	printf("\n");
	if(root->child != NULL) display(root->child);
}

void change_root_dir(char *file_name) {
	nary_pointer temp;
	temp = root_dir->child;

	if(strcmp(file_name,"..") == 0) {
		if(root_dir->parent == NULL) {
			//printf("Cannot go above the root.\n");
		}
		else root_dir = root_dir->parent;
		return;
	}

	while(temp != NULL) {
		if(strcmp(file_name, temp->file_name) == 0) {
			root_dir = temp;
			return;
		}
		else temp = temp->sibling;
	}
}

void convert_to_full_path(nary_pointer temp, char *str) {
	if(temp->parent != NULL){
        if(strcmp(temp->parent->file_name, "root/") != 0)
            convert_to_full_path(temp->parent, str);
        strcat(str,temp->file_name);
        if(temp->child != NULL) strcat(str,"/");
	}
	else {
        strcpy(str, temp->file_name);
	}

}

long int inc = 0;
void put_full_path(nary_pointer root) {
    // memset(root->filename,0,255);
    if(root == NULL) return;
    put_full_path(root->child);
	put_full_path(root->sibling);
    char str[256];

    memset(str, 0, 255);

    convert_to_full_path(root, str);

    strcpy(root->file_name,str);
    fd_array[inc].block_num = root->block_num;
    strcpy(fd_array[inc].file_name,root->file_name);
    fd_array[inc].child = root->child;
    fd_array[inc].parent = root->parent;
    fd_array[inc].sibling = root->sibling;
    fd_array[inc++].type = root->type;
}

void form_fd_array() {
    nary_pointer root;
    root = root_dir;

    if(fd_array != NULL) free(fd_array);

    make_fd_array();

	inc = 0;
    put_full_path(root);
}
